package com.amway.qy.core.service.impl;

import com.amway.qy.core.service.QyAccessTokenService;
import com.amway.qy.core.service.QyConcreteMessagePushService;
import com.amway.qy.push.message.PushEternalMPNewsMessage;
import com.amway.qy.push.message.PushFileMessage;
import com.amway.qy.push.message.PushImageMessage;
import com.amway.qy.push.message.PushMPNewsMessage;
import com.amway.qy.push.message.PushNewMessage;
import com.amway.qy.push.message.PushTextMessage;
import com.amway.qy.push.message.PushVideoMessage;
import com.amway.qy.push.message.PushVoiceMessage;
import com.amway.qy.push.message.result.QyPushMessageResult;
import com.amway.qy.util.common.QyHttpClientUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class QyDefaultMessagePushServiceImpl implements QyConcreteMessagePushService {
	private static final Logger logger = LoggerFactory.getLogger(QyDefaultMessagePushServiceImpl.class);
	private QyAccessTokenService accessTokenManager;

	public QyDefaultMessagePushServiceImpl(QyAccessTokenService accessTokenManager) {
		this.accessTokenManager = accessTokenManager;
	}

	private void check(Object msgData, Type targetType) {
		if (msgData.getClass()!=targetType.getClass()) {
			logger.error("msgData->" + msgData.getClass()+ "\t targetType->" + targetType.getClass());
			throw new RuntimeException("The msgData is incompliant with Target Type.");
		}
	}
	
	@Override
	public QyPushMessageResult sendMessage(Object msgData, Type targetType) {
		return new Gson().fromJson(sendMessageInRaw(msgData, targetType),QyPushMessageResult.class);
	}
	
	@Override
	public String sendMessageInRaw(Object msgData, Type targetType) {
		logger.info("sendMessageInRaw-->" + msgData.getClass());
		check(msgData, targetType);
		List<NameValuePair> paramsMap = new ArrayList<NameValuePair>();
		paramsMap.add(new BasicNameValuePair("access_token", this.accessTokenManager.getAccessToken()));
		String jsonData = convertPushMessageToJson(msgData);
		return QyHttpClientUtils.httpsPostJson("https://qyapi.weixin.qq.com/cgi-bin/message/send", paramsMap, jsonData);
	}
	
	/*
	 * @Description 
	 * 	由于企业号的数据格式，比如  userId是  xxx|xxx|xx 的方式来组装 目标接收着的。
	 * 	所以，为了方便操作，在应用层，应该以list<String>userId的方式去存放，但是对数据转换为JSON的时候，需要对list<string> userid做特殊处理。
	 */
	public String convertPushMessageToJson(Object msgData) {
		Gson gson = null;
		if(msgData instanceof PushTextMessage){
			gson = new GsonBuilder()
								.disableHtmlEscaping()
								.registerTypeAdapter(PushTextMessage.class, new PushTextSerialiser())
								.create();
		}else if (msgData instanceof PushImageMessage){
			gson = new GsonBuilder()
								.disableHtmlEscaping()
								.registerTypeAdapter(PushImageMessage.class, new PushImageSerialiser())
								.create();
		}else if (msgData instanceof PushVoiceMessage){
			gson = new GsonBuilder()
								.disableHtmlEscaping()
								.registerTypeAdapter(PushVoiceMessage.class, new PushVoiceSerialiser())
								.create();
		}else if (msgData instanceof PushVideoMessage){
			gson = new GsonBuilder()
								.disableHtmlEscaping()
								.registerTypeAdapter(PushVideoMessage.class, new PushVideoSerialiser())
								.create();
		}else if (msgData instanceof PushFileMessage){
			gson = new GsonBuilder()
								.disableHtmlEscaping()
								.registerTypeAdapter(PushFileMessage.class, new PushFileSerialiser())
								.create();
		}else if (msgData instanceof PushNewMessage){
			gson = new GsonBuilder()
								.disableHtmlEscaping()
								.registerTypeAdapter(PushNewMessage.class, new PushNewsSerialiser())
								.create();
		}else if (msgData instanceof PushMPNewsMessage){
			gson = new GsonBuilder()
								.disableHtmlEscaping()
								.registerTypeAdapter(PushMPNewsMessage.class, new PushMPNewsSerialiser())
								.create();
		}
		else{
			gson = new GsonBuilder().disableHtmlEscaping().create();
		}
		
		return gson.toJson(msgData);
	}
	
	@Override
	public QyPushMessageResult sendTextMessage(PushTextMessage textMessage) {
		Type type = new TypeToken<PushTextMessage>(){}.getType();
		return sendMessage(textMessage, type);
	}
	@Override
	public QyPushMessageResult sendVideoMessage(PushVideoMessage videoMessage) {
		Type type = new TypeToken<PushVideoMessage>(){}.getType();
		return sendMessage(videoMessage, type);
	}
	@Override
	public QyPushMessageResult sendVoiceMessage(PushVoiceMessage voiceMessage) {
		Type type = new TypeToken<PushVoiceMessage>(){}.getType();
		return sendMessage(voiceMessage, type);
	}
	@Override
	public QyPushMessageResult sendNewsMessage(PushNewMessage newsMessage) {
		Type type = new TypeToken<PushNewMessage>(){}.getType();
		return sendMessage(newsMessage, type);
	}
	@Override
	public QyPushMessageResult sendImageMessage(PushImageMessage imageMessage) {
		Type type = new TypeToken<PushImageMessage>(){}.getType();
		return sendMessage(imageMessage, type);
	}
	@Override
	public QyPushMessageResult sendFileMessage(PushFileMessage fileMessage) {
		Type type = new TypeToken<PushFileMessage>(){}.getType();
		return sendMessage(fileMessage, type);
	}
	@Override
	public QyPushMessageResult sendMPNewsMessage(PushMPNewsMessage mpNewsMessage) {
		Type type = new TypeToken<PushMPNewsMessage>(){}.getType();
		return sendMessage(mpNewsMessage, type);
	}
	@Override
	public QyPushMessageResult sendEternalMPNewsMessage(PushEternalMPNewsMessage eternalMPNewsMessage) {
		Type type = new TypeToken<PushEternalMPNewsMessage>(){}.getType();
		return sendMessage(eternalMPNewsMessage, type);
	}
	@Override
	public QyAccessTokenService getQyAccessTokenService() {
		return this.accessTokenManager;
	}
	@Override
	public void setQyAccessTokenService(QyAccessTokenService tokenService) {
		this.accessTokenManager = tokenService;
	}
	
	
	/*
	 * @description 
	 * 		自动对集合进行合并，主要是针对微信的格式化需求。
	 * 
	 * e.g
	 *   	应用层 数据结构 List user = {AA,BB}
	 *   转为后
	 * 		变成 user:"AA|BB"
	 */
	private static String convertListToString(List<String> ls){
		StringBuilder sb = new StringBuilder();
		for(int index =0; index<ls.size(); index++){
			if(index==0){
				sb.append(ls.get(index));
			}else{
				sb.append("|").append(ls.get(index));
			}
		}
		return sb.toString();
	}
	
	/*
	 * 以下为不同的JSON序列化器，由于此类序列化器只是用于该类调用，所以，不对外开放。
	 * 
	 */
	private static class PushTextSerialiser implements JsonSerializer<PushTextMessage>{

		@Override
		public JsonElement serialize(final PushTextMessage src, Type typeOfSrc, JsonSerializationContext context) {
			final JsonObject jsonObject = new JsonObject();
			
			jsonObject.addProperty("touser", convertListToString(src.getTouser()));
			jsonObject.addProperty("toparty", convertListToString(src.getToparty()));
			jsonObject.addProperty("totag", convertListToString(src.getTotag()));
			jsonObject.addProperty("msgtype", src.getMsgtype());
			jsonObject.addProperty("agentid", src.getAgentid());
			jsonObject.add("text", context.serialize(src.getText()));
			jsonObject.addProperty("safe", src.getSafe());
			return jsonObject;
		}	
    }
	
	private static class PushImageSerialiser implements JsonSerializer<PushImageMessage>{

		@Override
		public JsonElement serialize(PushImageMessage src, Type typeOfSrc, JsonSerializationContext context) {
			final JsonObject jsonObject = new JsonObject();
			
			
			jsonObject.addProperty("touser", convertListToString(src.getTouser()));
			jsonObject.addProperty("toparty", convertListToString(src.getToparty()));
			jsonObject.addProperty("totag", convertListToString(src.getTotag()));
			jsonObject.addProperty("msgtype", src.getMsgtype());
			jsonObject.addProperty("agentid", src.getAgentid());
			jsonObject.add("image", context.serialize(src.getImage()));
			jsonObject.addProperty("safe", src.getSafe());
			return jsonObject;
		}
		
	}
	
	private static class PushVoiceSerialiser implements JsonSerializer<PushVoiceMessage>{

		@Override
		public JsonElement serialize(PushVoiceMessage src, Type typeOfSrc, JsonSerializationContext context) {
			final JsonObject jsonObject = new JsonObject();
			
			jsonObject.addProperty("touser", convertListToString(src.getTouser()));
			jsonObject.addProperty("toparty", convertListToString(src.getToparty()));
			jsonObject.addProperty("totag", convertListToString(src.getTotag()));
			jsonObject.addProperty("msgtype", src.getMsgtype());
			jsonObject.addProperty("agentid", src.getAgentid());
			jsonObject.add("voice", context.serialize(src.getVoice()));
			jsonObject.addProperty("safe", src.getSafe());
			return jsonObject;
		}
		
	}
	
	private static class PushVideoSerialiser implements JsonSerializer<PushVideoMessage>{

		@Override
		public JsonElement serialize(PushVideoMessage src, Type typeOfSrc, JsonSerializationContext context) {
			final JsonObject jsonObject = new JsonObject();
			
			jsonObject.addProperty("touser", convertListToString(src.getTouser()));
			jsonObject.addProperty("toparty", convertListToString(src.getToparty()));
			jsonObject.addProperty("totag", convertListToString(src.getTotag()));
			jsonObject.addProperty("msgtype", src.getMsgtype());
			jsonObject.addProperty("agentid", src.getAgentid());
			jsonObject.add("video", context.serialize(src.getVideo()));
			jsonObject.addProperty("safe", src.getSafe());
			return jsonObject;
		}
		
	}
	
	private static class PushFileSerialiser implements JsonSerializer<PushFileMessage>{

		@Override
		public JsonElement serialize(PushFileMessage src, Type typeOfSrc, JsonSerializationContext context) {
			final JsonObject jsonObject = new JsonObject();
			
			jsonObject.addProperty("touser", convertListToString(src.getTouser()));
			jsonObject.addProperty("toparty", convertListToString(src.getToparty()));
			jsonObject.addProperty("totag", convertListToString(src.getTotag()));
			jsonObject.addProperty("msgtype", src.getMsgtype());
			jsonObject.addProperty("agentid", src.getAgentid());
			jsonObject.add("file", context.serialize(src.getFile()));
			jsonObject.addProperty("safe", src.getSafe());
			return jsonObject;
		}
		
	}
	
	private static class PushNewsSerialiser implements JsonSerializer<PushNewMessage>{

		@Override
		public JsonElement serialize(PushNewMessage src, Type typeOfSrc, JsonSerializationContext context) {
			final JsonObject jsonObject = new JsonObject();
			
			jsonObject.addProperty("touser", convertListToString(src.getTouser()));
			jsonObject.addProperty("toparty", convertListToString(src.getToparty()));
			jsonObject.addProperty("totag", convertListToString(src.getTotag()));
			jsonObject.addProperty("msgtype", src.getMsgtype());
			jsonObject.addProperty("agentid", src.getAgentid());
			jsonObject.add("news", context.serialize(src.getNews()));
			return jsonObject;
		}
		
	}
	
	private static class PushMPNewsSerialiser implements JsonSerializer<PushMPNewsMessage>{

		@Override
		public JsonElement serialize(PushMPNewsMessage src, Type typeOfSrc, JsonSerializationContext context) {
			final JsonObject jsonObject = new JsonObject();
			
			jsonObject.addProperty("touser", convertListToString(src.getTouser()));
			jsonObject.addProperty("toparty", convertListToString(src.getToparty()));
			jsonObject.addProperty("totag", convertListToString(src.getTotag()));
			jsonObject.addProperty("msgtype", src.getMsgtype());
			jsonObject.addProperty("agentid", src.getAgentid());
			jsonObject.add("mpnews", context.serialize(src.getMpnews()));
			jsonObject.addProperty("safe", src.getSafe());
			return jsonObject;
		}
		
	}
	
}
